%{
 /* import java.long.Math;
  import java.util.StringTokenizer; */
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;	// Used by the symbol table
import java.util.LinkedList; // Used to create phrases
import java.util.Iterator; // Used to iterate over lists

import upbeat.frontend.upbeatVal;
  
  class upbeatVal {
	  public String code;
	  
	  public int numMusicians;
	  
	  public enum Type { ubVOID, ubINT, ubFLOAT, ubBOOL, ubCUE, ubPHRASE, ubTIME, ubSTRING };
	  public Type type;
	  
	  public enum UnaryOp { uopPLUS, uopMINUS, uopNOT };
	  public UnaryOp unaryOp;
	  
	  public enum ToBy { To, By };
	  public ToBy toOrBy;
	  
	  public enum ChangeParam { Volume, Pitch, Instrument };
	  public ChangeParam changeParam;
	  
	  public LinkedList<String> listval;
	  
	  // This is used to list the types in the parameter list of functions
	  public LinkedList< upbeatVal.Type > listTypes;
	  public LinkedList< String > listExprs;
	  
	  // We were running into problems because we check to see if functions are defined at a different
	  // place as regular identifiers.  As a result, I had to move the check to see whether an identifier
	  // exists up a bit higher in the grammar.  As a result, we now have this value that tells whether
	  // or not we need to do a definition check.
	  public boolean definitionCheck;
	  
	  public int ival;
	  public double dval;
	  public boolean bval;
	  public String sval;
	  public Object obj;
	  
	  // These are used in range expressions.  rangeLeft and rangeRight
	  // are the strings corresponding with the expressions, and
	  // leftOpen and rightOpen tell whether the interval is open on the
	  // left and right
	  public String rangeLeft;
	  public boolean leftOpen;
	  public Type rangeLeftType;
	  public String rangeRight;
	  public boolean rightOpen;
	  public Type rangeRightType;
	  
	  // This is used in the postfix_expression productions
	  // so we can signal errors when the Upbeat programmer
	  // writes something like:
	  // Foo()()();
	  public boolean alreadyPostFixed;
	  
	  public boolean assignable;

	  public upbeatVal() { }
	  
	  public upbeatVal( ToBy t ) { toOrBy = t; }
	  
	  public upbeatVal( ChangeParam p ) { changeParam = p; }

	  public upbeatVal(int val) { ival=val; }

	  public upbeatVal(double val) { dval=val; }
	  
	  public upbeatVal(boolean val) { bval=val; }

	  public upbeatVal(String val) { code=val; }

	  public upbeatVal(Object val) { obj=val; }
	  
	  public upbeatVal(Type val) { type =val; }
	  
	  public upbeatVal(upbeatVal val) {
		  code = val.code;
		  numMusicians = val.numMusicians;
		  type = val.type;
		  toOrBy = val.toOrBy;
		  changeParam = val.changeParam;
		  ival = val.ival;
		  dval = val.dval;
		  sval = val.sval;
		  obj = val.obj;
	  }
	  
	  public upbeatVal clone() {
		  return new upbeatVal(this);
	  }
  }
  
  class FunctionType {
	  private LinkedList< upbeatVal.Type > mArgTypes;
	  private upbeatVal.Type mReturnType;
	  
	  public FunctionType() {
		  mArgTypes = new LinkedList< upbeatVal.Type >();
		  mReturnType = null;
	  }
	  
	  public void SetReturnType( upbeatVal.Type t ) {
		  mReturnType = t;
	  }
	  
	  public void SetArgs( LinkedList< upbeatVal.Type > args ) {
		  mArgTypes = args;
	  }
	  
	  public void AddArg( upbeatVal.Type arg ) {
		  mArgTypes.add( arg );
	  }
	  
	  public upbeatVal.Type GetReturnType() {
		  return mReturnType;
	  }
	  
	  public LinkedList< upbeatVal.Type > GetArgs() {
		  return mArgTypes;
	  }
	  
	  public boolean CheckArgTypes( LinkedList< upbeatVal.Type > args ){
		  Iterator< upbeatVal.Type > argIter = args.iterator();
		  Iterator< upbeatVal.Type > mArgIter = mArgTypes.iterator();
		  
		  // If either of the types don't match up, return false
		  while( argIter.hasNext() && mArgIter.hasNext() ) {
		      if( argIter.next() != mArgIter.next() )
		    	  return false;
		  }
		      
		  // If the number of arguments doesn't match up, return false
		  if( argIter.hasNext() || mArgIter.hasNext() )
			  return false;
		  
		  return true;
	  }
  }
  
  // Pretty simple symbol table.  Can be expanded if need be
  class SymbolTable {
	  private Hashtable< String, upbeatVal.Type > mSymbols;
	  private Hashtable< String, Integer > mDefLine;
	  
	  private Hashtable< String, FunctionType > mFuncSymbols;
	  private Hashtable< String, Integer > mFuncDefLine;
	  
	  public SymbolTable parent;
	  
	  public boolean inConductor;
	  public boolean inConductorFunc;
	  public boolean inMusician;
	  public boolean inCueResponse;
	  public boolean inRepeat;
	  
	  // This is the name of the function we are in, if it exists...
	  public String myName;
	  public boolean returns;
	  
	  SymbolTable() {
		  // special top-level symbol table
		  parent = null;
		  inConductor = false;
		  inConductorFunc = false;
		  inMusician = false;
		  inCueResponse = false;
		  inRepeat = false;
		  myName = null;
		  mSymbols = new Hashtable<String, upbeatVal.Type>();
		  mDefLine = new Hashtable<String, Integer>();
		  mFuncSymbols = new Hashtable< String, FunctionType >();
		  mFuncDefLine = new Hashtable< String, Integer >();
	  }
	  SymbolTable(SymbolTable p) {
		  parent = p;
		  inConductorFunc = p.inConductorFunc;
		  inConductor = p.inConductor;
		  inMusician = p.inMusician;
		  inCueResponse = p.inCueResponse;
		  inRepeat = p.inRepeat;
		  myName = p.myName;
		  mSymbols = new Hashtable<String, upbeatVal.Type>();
		  mDefLine = new Hashtable<String, Integer>();
		  mFuncSymbols = new Hashtable< String, FunctionType >();
		  mFuncDefLine = new Hashtable< String, Integer >();
	  }
	  
	  public void Returns() {
		  SymbolTable i = this;
		  
		  while( i.inConductorFunc && i.myName != null && i != null) {
			  i.returns = true;
			  i = i.parent;
		  }
		  
		  if( i.inConductorFunc || i.myName != null || i == null ) {
			  throw new Error( "Cannot figure out which function we are returning from" );
		  }
	  }
	  
	  public upbeatVal.Type GetCurrentReturnType() {
		  return GetFuncReturnType( myName );
	  }
	  
	  public void AddSymbol( String name, upbeatVal.Type type, int line ) {
		  mSymbols.put( name, type );
		  mDefLine.put( name, line );
	  }
	  
	  public void AddFuncSymbol( String name, FunctionType type, int line ) {
		  mFuncSymbols.put( name, type );
		  mFuncDefLine.put( name, line );
	  }
	  
	  public int numSymbols() {
		  if(parent != null) {
			  return mSymbols.size() + parent.numSymbols();
		  }
		  return mSymbols.size();
	  }
	  
	  public int numFuncSymbols() {
		  if(parent != null) {
			  return mFuncSymbols.size() + parent.numFuncSymbols();
		  }
		  return mFuncSymbols.size();
	  }
	  
	  // returns null if the name does not exist!
	  public upbeatVal.Type GetType( String name ) {
		  SymbolTable i = this;
		  while (i != null && i.mSymbols.get(name) == null) {
			  i = i.parent;
		  }
		  if (i==null) {
			  // no symbol by that name.
			  return null;
		  }
		  return i.mSymbols.get( name );
	  }
	  
	  // returns null if the name does not exist!
	  public upbeatVal.Type GetFuncReturnType( String name ) {
		  SymbolTable i = this;
		  while (i != null && i.mFuncSymbols.get(name) == null) {
			  i = i.parent;
		  }
		  if (i==null) {
			  // no symbol by that name.
			  return null;
		  }
		  return i.mFuncSymbols.get( name ).GetReturnType();
	  }
	  
	  public boolean CheckFuncArgTypes( String name,  LinkedList< upbeatVal.Type > argTypes ) {
		  SymbolTable i = this;
		  while (i != null && i.mFuncSymbols.get(name) == null) {
			  i = i.parent;
		  }
		  if (i==null) {
			  // no symbol by that name.
			  return false;
		  }
		  return i.mFuncSymbols.get( name ).CheckArgTypes( argTypes );
	  }
	  
	  public LinkedList< upbeatVal.Type > GetParamTypes( String name ) {
		  SymbolTable i = this;
		  while (i != null && i.mFuncSymbols.get(name) == null) {
			  i = i.parent;
		  }
		  if (i==null) {
			  // no symbol by that name.
			  return null;
		  }
		  return i.mFuncSymbols.get( name ).GetArgs();
	  }
	  
	  public int GetDefLine( String name ) {
		  SymbolTable i = this;
		  while (i != null && i.mDefLine.get(name) == null) {
			  i = i.parent;
		  }
		  if (i==null) {
			  // no symbol by that name.
			  return -1;
		  }
		  return i.mDefLine.get( name ); 
	  }
	  
	  public int GetFuncDefLine( String name ) {
		  SymbolTable i = this;
		  while (i != null && i.mFuncDefLine.get(name) == null) {
			  i = i.parent;
		  }
		  if (i==null) {
			  // no symbol by that name.
			  return -1;
		  }
		  return i.mFuncDefLine.get( name ); 
	  }
	  
	  public String toString() {
		  String blah = new String();
		  Enumeration<String> keys = mSymbols.keys();
		  while(keys != null && keys.hasMoreElements()) {
			  String k = keys.nextElement();
			  blah = blah + "Var: " + k + " Type: " + mSymbols.get(k) + "\n";
		  }
		  return blah;
	  }
  }
%}   

/* Declarations */
%token IDENTIFIER CONSTANT STRING_LITERAL
// Special identifier: $
// CONSTANT: 1234(int) 1.234(float) @m1234(Time) REST(Phrase) TRUE(bool)
// STRING_LITERAL "1234"
%right  EQ_ASS
//       =
%left AND_OP OR_OP
//	  and    or
%left EQ_OP NE_OP
//    ==    !=
%left LE_OP GE_OP LESS_OP GREAT_OP
//     <=    >=    <        >
%left '+' '-' 
%left '%'
%left '*' '/'

// Basic Types
%token INT FLOAT BOOL CUE PHRASE TIME INPUT
//     int float bool cue phrase time input

// Special types
%token INPUT CURR_CUE

//Attributes
%token CURRENT_VALUE DATA MORE VALUE_AT 
//     currentValue  data more valueAt

%token CASE RETURN REPEAT BREAK CONTINUE
//     Case Return Repeat Break Continue

%token TIME_SIGNATURE TEMPO
//     TIME_SIGNATURE TEMPO
 
%token ADD CONDUCTOR MUSICIAN
//     Add Conductor Musician

%token IN
//     in

%token CHANGE VOLUME INSTRUMENT PITCH TO BY
//     Change Volume Instrument Pitch to by

%token DB RRPT //REST LRPT
//     || :||  rest    ||:  we have to keep || and : separate   

%start upbeat_src   

//Here's the grammar
%%

upbeat_src
    : header conductor_block musician_blocks
    	{
			$$ = new upbeatVal("Generating compiled Upbeat code for " + $3.ival + " musicians.\n");
			
			String app_code = new String();
			
			app_code = app_code.concat("package upbeat.sample;\n\n" +
					"import upbeat.jmusiclib.*;\n" +
					"import upbeat.*;\n\n" +
					"public class CompiledUpbeatApplication implements UpbeatApplication {\n" +
					"	public static void main( String [] args ) {\n" +
"		// Right now, we're just going to assume that there is one input argument, and that it is a file name.\n" +
"		// When we implement real time, we will need to check to see if there is an additional flag to see\n" +
"		// whether we want real time or not.  The implementation of the Manager class we instantiate here will\n" +
"		// be dependent on whether or not we want to run in real time.  However, for now, just assume it's always\n" +
"		// offline.\n" +
"\n" +		
"		// Create the Manager, Conductor, and Musician(s).\n" +
"		Manager manager;\n" +
"		//if( realtime )\n" +
"		// manager = new RealTimeManager();\n" +
"		//else\n" +
"		manager = new OfflineManager();\n" +
"\n" +		
"		TempoOptions tempoOptions = new CompiledTempoOptions();\n" +
"		Conductor conductor = new CompiledConductor(manager);\n");
			
			Hashtable<String, String> musicians;
			Enumeration<String> k;
			// TODO: clean up the way we assign musician code
			musicians = (Hashtable)$3.obj;
			k = musicians.keys();
			  while(k != null && k.hasMoreElements()) {
				  String key = k.nextElement();
				  app_code = app_code.concat("\t\tMusician " + key + " = new Musician_" + key + "();\n");
			  }
			
			app_code = app_code.concat(
"		// Set the manager up with the input file, the Conductor, and the Musician(s)\n" +
"		if( args.length < 1 ) {\n" +
"			System.out.println(\"Warning: No input source supplied!\");\n" +
"		}\n" +
"		else {\n" +
"			String inputname = args[0];\n" +
"			if( !manager.SetInputSource(inputname) ) {\n" +
"				System.out.printf(\"Error: Could not open source:%s\", inputname);\n" +
"			}\n" +
"		}\n" +
"\n" +			
"		manager.SetJMusicWrapper(new JMusicWrapper());\n" +
"		manager.SetTempoOptions(tempoOptions);\n" +
"		manager.SetConductor(conductor);\n" +
"\n" +				
"		// We're going to need to have an individual AddMusician call for each Musician\n");
			
			k = musicians.keys();
			  while(k != null && k.hasMoreElements()) {
				  String key = k.nextElement();
				  app_code = app_code.concat("\t\tmanager.AddMusician(" + key + ");\n");
			  }

			  app_code = app_code.concat(
"		// Note, if we're writing the output to a MIDI file on disk we might want to have a way of\n" +
"		// providing an output file name.\n" +

"		try {\n" +
"		manager.MakeSomeMusic();\n" +
"		} catch (Exception e) {\n" +
"		};\n" +
"	}\n" +
"}\n\n");

			app_code = app_code.concat($1.code);
			
			String conductor_code = new String($2.code);

			//System.out.println(">>>>>>>>>>>>> CompiledUpbeatApplication.java >>>>>>>>>>>>>>>>>\n\n" + app_code +
			//		"\n>>>>>>>>>>>>> CompiledConductor.java >>>>>>>>>>>>>>>>>\n\n" + conductor_code);
			k = musicians.keys();
			while(k != null && k.hasMoreElements()) {
				String key = k.nextElement();
			//	System.out.println(">>>>>>>>>>>>> Musician_" + key + ".java >>>>>>>>>>>>>>>>>\n\n" + musicians.get(key) + "\n\n");
			}
			/* 1. Construct CompiledUpbeatApplication:
			 * 	a. iterate through musicians by NAME and construct Musician NAME = new Musician_NAME()
			 * 	b. also iterate through and do manager.addMusician(NAME);
			 *  c. stick the tempo options on the end!
			 * 2. Construct CompiledConductor
			 *  a. stick in 
			 */
			if(current_symbol_table.numSymbols() > 0) {
				System.err.println("Warning: global symbol table has symbols; this is unusual in Upbeat:\n" + current_symbol_table.toString());
			}
			$$ = new upbeatVal(app_code);
			$$.sval = conductor_code;
			$$.obj = new Hashtable<String, String>();
			k = musicians.keys();
			while(k != null && k.hasMoreElements()) {
				String key = k.nextElement();
				((Hashtable<String,String>)$$.obj).put(key, musicians.get(key));
			}
    	}
    ;
 
header
    : header_time_sig header_tempo
    	{ $$ = new upbeatVal("class CompiledTempoOptions extends TempoOptions {\n" +
    	"\tCompiledTempoOptions(){\n\t\t// This is where and how we set the header information\n" +
    	$1.code + $2.code +
    	"\t}\n}\n"); }
	| header_tempo header_time_sig
		{ $$ = new upbeatVal("class CompiledTempoOptions extends TempoOptions {\n" +
	    "\tCompiledTempoOptions(){\n\t\t// This is where and how we set the header information\n" +
	    $2.code + $1.code +
	    "\t}\n}\n"); }
    ;
 
header_time_sig
    : TIME_SIGNATURE CONSTANT '/' CONSTANT ';'
    	{ $$ = new upbeatVal("\t\tsuper.TIME_SIGNATURE_TOP = " + $2.ival +
    						 ";\n\t\tsuper.TIME_SIGNATURE_BOTTOM = " + $4.ival + ";\n"); }
    ;
 
header_tempo
    : TEMPO CONSTANT ';'
    	{
    	if ($2.ival < 1 || $2.ival > 1000) {
    	//TODO: what are the legal tempo values?
    	//TODO: define a warning and an error function for parsing? lexing?
    		System.err.print("Warning: TEMPO must be an integer between 1 and 1000, representing beats per minute.  " +
    		"Defaulting to 100 bpm.\n");
    		$2.ival = 100;
    	}
    	$$ = new upbeatVal("\t\tsuper.TEMPO = " + $2.ival + ";\n"); }
    ;
 
conductor_block
    : CONDUCTOR '{'
    	{
    		SymbolTable t = new SymbolTable(current_symbol_table);
    		current_symbol_table = t;
    		current_symbol_table.inConductor = true;
    	}
    conductor_body '}'
    	{
    	$$ = new upbeatVal($4.code);
    	current_symbol_table = current_symbol_table.parent;
    		/* 
    		 * Write out CompiledConductor class with:
    		 * 1. Declarations for all local variables in Upbeat Conductor {} block
    		 * 2. Constructors for CompiledConductor class that initialize all local Upbeat variables
    		 * 3. All functions defined in Conductor {} block
    		 * 4. 
    		 */
    	}
    | error '}'
    {
    	System.err.println("Error in Conductor.");
    	if( current_symbol_table != null )
    		current_symbol_table = current_symbol_table.parent;
    }
    ;
 
conductor_body
    : function_definition_list
    {
    	String declarations = new String("");
    	// there are no declarations
    	String statements = new String("");
    	// there are no statements
    	String functions = new String("");
    	functions = functions.concat($1.code);
    	
    	String compiled_code = generate_conductor_code(declarations, statements, functions);

    	$$ = new upbeatVal(compiled_code);
    	/* TODO: Check to make sure there's at least 1 Conduct() method
    	 * 
    	 */
    }
    | statement_list function_definition_list
    {
    	String declarations = new String("");
    	// there are no declarations
    	String statements = new String($1.code);
    	String functions = new String("");
    	functions = functions.concat($2.code);
    	
    	String compiled_code = generate_conductor_code(declarations, statements, functions);

    	$$ = new upbeatVal(compiled_code);
    	/* TODO: Check to make sure there's at least 1 Conduct() method
    	 * 
    	 */
    }
    | declaration_list function_definition_list  
    {
    	String declarations = new String($1.code);
    	String statements = new String("");
    	// there are no statements
    	String functions = new String($2.code);
    	
    	String compiled_code = generate_conductor_code(declarations, statements, functions);

    	$$ = new upbeatVal(compiled_code);
    	/* TODO: Check to make sure there's at least 1 Conduct() method
    	 * 
    	 */
    }  
    | declaration_list statement_list function_definition_list
    {
    	String declarations = new String($1.code);
    	String statements = new String($2.code);
    	String functions = new String($3.code);
    	
    	String compiled_code = generate_conductor_code(declarations, statements, functions);

    	$$ = new upbeatVal(compiled_code);
    	/* TODO: Check to make sure there's at least 1 Conduct() method
    	 * 
    	 */
    }
    | error '}'
    {
    	System.err.println("Error in Conductor body.");
    }
    | error STRING_LITERAL
    {
    	System.err.println("Error in Conductor body.  Missing closing } perhaps?");
    }
    ; 
 
compound_statement
    : '{' '}'
    	{
    		$$ = new upbeatVal("{ }");
    	}
    | '{' statement_list '}'
    	{
    		// No Symbol table needed, since there are no declarations.
    		$$ = new upbeatVal("{\n" + $2.code + " }\n");
    	}
    | '{' 
		{ // start new symbol table:
    		//  1. create new symbol table new_table
    		//  2. assign current_sym_table to be its parent
    		//  3. assign new_table to current_sym_table
  			SymbolTable t = new SymbolTable(current_symbol_table);
  			current_symbol_table = t;
		}
    declaration_list statement_list '}'
    	{
    		$$ = new upbeatVal("{\n" + $3.code + "\n" + $4.code + " }\n");
    	  	current_symbol_table = current_symbol_table.parent;
    	}
    ;
 
statement_list
    : statement
    {
    	$$ = new upbeatVal($1.code);
    }
    | statement_list statement
    {
    	$$ = new upbeatVal($1.code + $2.code);
    }
    ;
 
statement
    : compound_statement
    	{
    		$$ = new upbeatVal($1.code);
    	}
    | expression_statement
		{
    		$$ = new upbeatVal($1.code);
		}
    | adding_cue_statement
		{
    		$$ = new upbeatVal($1.code);
		}
    | musical_statement
		{
    		$$ = new upbeatVal($1.code);
		}
    | flow_control_statement
		{
    		$$ = new upbeatVal($1.code);
		}
    | error ';'
    {
    	System.err.println("Error in statement.");
    }
    ;

flow_control_statement
    : case_statement
    | repeat_statement
    | return_statement
    | continue_statement
    | break_statement
    ;

continue_statement
	: CONTINUE ';'
		{
	    	if( !current_symbol_table.inRepeat )
	    		yyerror("Continue statement outside of a Repeat statement");
	    	
	    	$$ = new upbeatVal("continue;\n");
		}
    ;
    
break_statement
	: BREAK ';'
		{
	    	if( !current_symbol_table.inRepeat )
	    		yyerror("Break statement outside of a Repeat statement");
	    	
	    	$$ = new upbeatVal("break;\n");
		}
    ;
    
return_statement
    : RETURN ';'
    	{
    		if( !current_symbol_table.inConductorFunc )
    			yyerror("Return statement outside of a function definition");
    		else {
    			if( current_symbol_table.GetCurrentReturnType() != upbeatVal.Type.ubVOID )
	    			yyerror("Function " + current_symbol_table.myName + " requires a return value" );
	    		
    			current_symbol_table.Returns();
    		}
    		$$ = new upbeatVal("return;\n");
    	}
    | RETURN expression ';'
    	{
    		if( !current_symbol_table.inConductorFunc ) {
    			yyerror("Return statement outside of a function definition");
    			// this is a dummy for now
    			$$ = new upbeatVal("return;\n");
    		}
    		else {
	    		$$ = new upbeatVal("return " + perform_cast( $2.type, current_symbol_table.GetCurrentReturnType() ) + $2.code + ";\n");
	    		current_symbol_table.Returns();
    		}
    	}
    ;
 
expression_statement
    : expression ';'
    	{
    		$$ = new upbeatVal($1.code + ";\n");
    	}
    | ';'
    	{
    		$$ = new upbeatVal(";\n");
    	}
    ;
 
case_statement
    : CASE 
	    {
    		if( !current_symbol_table.inConductorFunc 
       		 && !current_symbol_table.inCueResponse )
       			yyerror("Case statement outside a function definition or cue response");
	    }
    '{' case_list '}'
	    {
	    	$$ = new upbeatVal( $4.code );
	    }
    | CASE
	    {
	    	if( !current_symbol_table.inConductorFunc 
	       	&& !current_symbol_table.inCueResponse )
	       		yyerror("Case statement outside a function definition or cue response");
	    }
		case
	    {
	    	$$ = new upbeatVal( $3.code );
	    }
    ;
 
case_list
    : case
    	{
    		$$ = new upbeatVal($1.code);
    	}
    | case case_list
	    {
	    	String newCode = new String("");
	    	newCode = newCode.concat( $1.code );
	    	newCode = newCode.concat( "else " );
	    	newCode = newCode.concat( $2.code );
	    	
	    	$$ = new upbeatVal( newCode );
	    }
    ;
 
case
    : '(' expression ')' statement
	    {
	    	String newCode = new String("if(");
	    	newCode = newCode.concat($2.code);
	    	newCode = newCode.concat(") {\n");
	    	newCode = newCode.concat( $4.code );
	    	newCode = newCode.concat( "}\n" );
	    	
	    	$$ = new upbeatVal( newCode );
	    }
    ;

repeat_statement
    : REPEAT '(' expression ')'   
	{
    	if( !current_symbol_table.inConductorFunc 
          	&& !current_symbol_table.inCueResponse )
          	yyerror("Repeat statement outside a function definition or cue response");
		if ($3.type == upbeatVal.Type.ubBOOL) {
			String newCode = new String ("while (");
			newCode = newCode.concat ($3.code);
			newCode = newCode.concat (") {\n");
			$$ = new upbeatVal( newCode );
    	}
		else {
			String ub_REPEATlimit = new String( getNextTempName() ); // need to get the name to use later
			String newCode = new String("int " + ub_REPEATlimit + " = " + perform_cast($3.type, upbeatVal.Type.ubINT) + $3.code  + ";\n");
			// now generate for loop
    		newCode = newCode.concat ("for (int ub_REPEATscroll = 1; ub_REPEATscroll <= " + ub_REPEATlimit + "; ub_REPEATscroll++ ) {\n");
    		$$ = new upbeatVal( newCode );
		}
		
		SymbolTable t = new SymbolTable(current_symbol_table);
		t.inRepeat = true;
		current_symbol_table = t;
	}
    statement 
    {
    	$$ = new upbeatVal( $5.code + $6.code + " }\n" );
    	
    	// We need to reset the symbol table
    	current_symbol_table = current_symbol_table.parent;
    }
    ;

// Expressions
expression
    : logical_or_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | postfix_expression EQ_ASS expression
      	{
    		if( !$1.assignable ) {
    			yyerror("Cannot assign to expression to left of =");
    			if( $1.code != null && $1.code.charAt(0) == '(' && $1.code.charAt($1.code.length() - 1) == ')' )
    				yyerror("If it is parenthesized, try removing the parentheses");
    		}

    		// If we haven't checked to make sure we've defined the stuff used in the
    		// post fix expression, we need to do the check now!
    		if( !$1.definitionCheck ) {
    			if( current_symbol_table.GetDefLine( $1.sval ) == -1 ) {
    				yyerror("Identifier: " + $1.sval + " is undefined");
    				if($1.sval.toLowerCase().equals("return")) {
    					yyerror("Perhaps you meant \"Return\" with a capital \"R\"?");
    				} else if ($1.sval.toLowerCase().equals("input")) {
    					yyerror("Perhaps you meant \"input\", all lowercase?");
    				} // else no help
    			}
    		}

			$$ = apply_op("=", $1.code, $3.code, $1.type, $3.type);
			/* TODO: (optional) mark in symbol table that it's been assigned */
      	}
    ;
 
constant_expression
    : logical_or_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    ;
    
logical_or_expression
    : logical_and_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | logical_or_expression OR_OP logical_and_expression
	    {
			$$ = apply_op("||", $1.code, $3.code, $1.type, $3.type);
	  	}
    ;
 
logical_and_expression
    : equality_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | logical_and_expression AND_OP equality_expression
	    {
			$$ = apply_op("&&", $1.code, $3.code, $1.type, $3.type);
	  	}
    ;
    
equality_expression
    : relational_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | equality_expression EQ_OP relational_expression
	    {
			$$ = apply_op("==", $1.code, $3.code, $1.type, $3.type);
	  	}
    | equality_expression NE_OP relational_expression
	    {
			$$ = apply_op("!=", $1.code, $3.code, $1.type, $3.type);
	  	}
    ;

relational_expression
    : range_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | relational_expression LESS_OP range_expression
	    {
			$$ = apply_op("<", $1.code, $3.code, $1.type, $3.type);
	  	}
    | relational_expression GREAT_OP range_expression
	    {
			$$ = apply_op(">", $1.code, $3.code, $1.type, $3.type);
	  	}
    | relational_expression LE_OP range_expression
	    {
			$$ = apply_op("<=", $1.code, $3.code, $1.type, $3.type);
	  	}
    | relational_expression GE_OP range_expression
	    {
			$$ = apply_op(">=", $1.code, $3.code, $1.type, $3.type);
	  	}
    ;

range_expression
    : additive_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | additive_expression IN range_specifier
	    {
    		if( $1.type != upbeatVal.Type.ubINT && $1.type != upbeatVal.Type.ubFLOAT ) {
    			yyerror("Expression to left of 'in' keyword on line does not evaluate to an " +
    					upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT) + ".");
    		}
    		
	    	upbeatVal.Type tempType;
	    	if( $1.type == upbeatVal.Type.ubINT && 
	    	    $3.rangeLeftType == upbeatVal.Type.ubINT &&
	    	    $3.rangeRightType == upbeatVal.Type.ubINT )
	    		tempType = upbeatVal.Type.ubINT;
	    	else
	    		tempType = upbeatVal.Type.ubFLOAT;
	    	
	    	// Lets get our additive expression code right
	    	// We need to make sure to cast it if need be...
	    	String addExpr = new String("(");
	    	if( $1.type != tempType )
	    		addExpr = addExpr.concat( "(" + javaTypeName(tempType) + ")" );
	    	addExpr = addExpr.concat($1.code);
	    	addExpr = addExpr.concat(")");
	    	
	    	// Do the same for our right and left expressions in our
	    	// range specifier
	    	String leftExpr = new String("(");
	    	if( $3.rangeLeftType != tempType )
	    		leftExpr = leftExpr.concat( "(" + javaTypeName(tempType) + ")" );
	    	leftExpr = leftExpr.concat($3.rangeLeft);
	    	leftExpr = leftExpr.concat(")");
	    	
	    	String rightExpr = new String("(");
	    	if( $3.rangeRightType != tempType )
	    		rightExpr = rightExpr.concat( "(" + javaTypeName(tempType) + ")" );
	    	rightExpr = rightExpr.concat($3.rangeRight);
	    	rightExpr = rightExpr.concat(")");
	    	
	    	// Finally, we need to make sure our operations are correct
	    	// depending on whether the range is open or closed.
	    	String leftOp;
	    	if( $3.leftOpen )
	    		leftOp = new String(" > ");
	    	else
	    		leftOp = new String(" >= ");
	    	
	    	String rightOp;
	    	if( $3.rightOpen )
	    		rightOp = new String(" < ");
	    	else
	    		rightOp = new String(" <= ");
	    	
	    	String newCode = new String("((");
	    	newCode = newCode.concat( addExpr + leftOp + leftExpr );
	    	newCode = newCode.concat( ") && (");
	    	newCode = newCode.concat( addExpr + rightOp + rightExpr );
	    	newCode = newCode.concat("))");
	    	
	    	$$ = new upbeatVal(newCode);
	    	$$.type = upbeatVal.Type.ubBOOL;
	    }
    ;

range_specifier
    : '(' expression ',' expression ')'
	    {
	    	if( $2.type != upbeatVal.Type.ubINT && 
	    		$2.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Left half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	if( $4.type != upbeatVal.Type.ubINT && 
	    		$4.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Right half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	
	    	$$ = new upbeatVal();
	    	$$.rangeLeft = new String( $2.code );
	    	$$.rangeRight = new String( $4.code );
	    	$$.leftOpen = true;
	    	$$.rightOpen = true;
	    	$$.rangeLeftType = $2.type;
	    	$$.rangeRightType = $4.type;
	    }
    | '[' expression ',' expression ')'
	    {
	    	if( $2.type != upbeatVal.Type.ubINT && 
	    		$2.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Left half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	if( $4.type != upbeatVal.Type.ubINT && 
	    		$4.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Right half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	
	    	$$ = new upbeatVal();
	    	$$.rangeLeft = new String( $2.code );
	    	$$.rangeRight = new String( $4.code );
	    	$$.leftOpen = false;
	    	$$.rightOpen = true;
	    	$$.rangeLeftType = $2.type;
	    	$$.rangeRightType = $4.type;
	    }
    | '(' expression ',' expression ']'
	    {
	    	if( $2.type != upbeatVal.Type.ubINT && 
	    		$2.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Left half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	if( $4.type != upbeatVal.Type.ubINT && 
	    		$4.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Right half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	
	    	$$ = new upbeatVal();
	    	$$.rangeLeft = new String( $2.code );
	    	$$.rangeRight = new String( $4.code );
	    	$$.leftOpen = true;
	    	$$.rightOpen = false;
	    	$$.rangeLeftType = $2.type;
	    	$$.rangeRightType = $4.type;
	    }
    | '[' expression ',' expression ']'
	    {
	    	if( $2.type != upbeatVal.Type.ubINT && 
	    		$2.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Left half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	if( $4.type != upbeatVal.Type.ubINT && 
	    		$4.type != upbeatVal.Type.ubFLOAT ) {
	    		yyerror("Right half of range specifier does not evaluate to an " 
	    				+ upbeatTypeName(upbeatVal.Type.ubINT) + " or a " + upbeatTypeName(upbeatVal.Type.ubFLOAT));
	    	}
	    	
	    	$$ = new upbeatVal();
	    	$$.rangeLeft = new String( $2.code );
	    	$$.rangeRight = new String( $4.code );
	    	$$.leftOpen = false;
	    	$$.rightOpen = false;
	    	$$.rangeLeftType = $2.type;
	    	$$.rangeRightType = $4.type;
	    }
    ;
 
additive_expression
    : multiplication_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | additive_expression '+' multiplication_expression
    	{
    		$$ = apply_op("+", $1.code, $3.code, $1.type, $3.type);
    	}	
    | additive_expression '-' multiplication_expression
	    {
	    	$$ = apply_op("-", $1.code, $3.code, $1.type, $3.type);
	    }
    ;
 
multiplication_expression
    : cast_expression
		{
			$$ = new upbeatVal( $1.code );
			$$.type = $1.type;
		}
    | multiplication_expression '*' cast_expression
	    {
	    	$$ = apply_op("*", $1.code, $3.code, $1.type, $3.type);
	    }
    | multiplication_expression '/' cast_expression
	    {
    		$$ = apply_op("/", $1.code, $3.code, $1.type, $3.type);
	    }
    | multiplication_expression '%' cast_expression
	    {
			$$ = apply_op("%", $1.code, $3.code, $1.type, $3.type);
	    }
    ;

cast_expression
    : postfix_expression
    	{
    		$$ = new upbeatVal( $1.code );
    		$$.type = $1.type;
    		
    		// If we haven't checked to make sure we've defined the stuff used in the
    		// post fix expression, we need to do the check now!
    		if( !$1.definitionCheck ) {
    			if( current_symbol_table.GetDefLine( $1.sval ) == -1 ) {
    				yyerror("Identifier: " + $1.sval + " is undefined");
    				if($1.sval.toLowerCase().equals("return")) {
    					yyerror("Perhaps you meant \"Return\" with a capital \"R\"?");
    				} else if ($1.sval.toLowerCase().equals("input")) {
    					yyerror("Perhaps you meant \"input\", all lowercase?");
    				} // else no help
    			}
    		}
    			
    	}
    | unary_operator cast_expression
	    {
	    	$$ = new upbeatVal( $1.code + $2.code );
	    	$$.type = $2.type;
	    	
	    	switch( $1.unaryOp ) {
	    	case uopPLUS:
	    		switch( $2.type ) {
	    		case ubVOID:
	    		case ubBOOL:
	    		case ubCUE:
	    		case ubPHRASE:
	    		case ubSTRING:
	    			yyerror("Attempting to apply unary operator '+' to an expression of type: "
	    					+ upbeatTypeName($2.type));
	    		}
	    		break;
	    	case uopMINUS:
	    		switch( $2.type ) {
	    		case ubVOID:
	    		case ubBOOL:
	    		case ubCUE:
	    		case ubPHRASE:
	    		case ubSTRING:
	    			yyerror("Attempting to apply unary operator '-' to an expression of type: "
	    					+ upbeatTypeName($2.type));
	    		}
	    		break;
	    	case uopNOT:
	    		switch( $2.type ) {
	    		case ubVOID:
	    		case ubINT:
	    		case ubFLOAT:
	    		case ubCUE:
	    		case ubPHRASE:
	    		case ubTIME:
	    		case ubSTRING:
	    			yyerror("Attempting to apply unary operator '!' to an expression of type: "
	    					+ upbeatTypeName($2.type));
	    		}
	    		break;
	    	}
	    }
    | '(' type_specifier ')' cast_expression
	    {
    		String newCode = new String( force_cast($4.type, $2.type) );
    		newCode = newCode.concat( $4.code );
	    	$$ = new upbeatVal(newCode);
	    	$$.type = $2.type;
	    }
    ;
 
    // These guys will be tricky because we need to create a symbol table
    // for function definitions so we can lookup their return types here.
    // Otherwise, our type tracking will get all screwed up.
    // Since YACC reads the source from top to bottom, we might need to
    // force the programmers to write their member functions above where
    // they're called.
postfix_expression
    : primary_expression
    	{
    		$$ = new upbeatVal( $1.code );
    		$$.type = $1.type;
    		$$.sval = $1.sval;
    		$$.alreadyPostFixed = false;
    		
    		// Up the line we might need to do a definition check
    		$$.definitionCheck = $1.definitionCheck;
    		$$.assignable = $1.assignable;
    	}
    | input_expression
	    {
	    	$$ = new upbeatVal( $1.code );
	    	$$.type = $1.type;
	    	
	    	// Don't think we need this here
	    	//$$.sval = $1.sval;
	    	
	    	// We never want to add a postfix operator after an input expression
	    	$$.alreadyPostFixed = true;
	    	$$.definitionCheck = true;
	    	$$.assignable = false;
	    }
    | postfix_expression '(' ')'
	    {
	    	if( $1.alreadyPostFixed )
				yyerror("Chaining of postfix expressions not allowed");
		    
		    // Now let's do the type checking!
			if( current_symbol_table.GetFuncDefLine( $1.sval ) == -1 ) {
				yyerror("Function: " + $1.sval + " is undefined");
				$$ = $1;
			}
			else
				$$ = CreateFunctionCall( $1 );
			
			
			$$.alreadyPostFixed = true;
			$$.definitionCheck = true;
			$$.assignable = false;
	    }
    | postfix_expression '(' argument_expression_list ')'
	    {
	    	if( $1.alreadyPostFixed )
				yyerror("Chaining of postfix expressions not allowed");

	    	// Now let's do the type checking!
			if( current_symbol_table.GetFuncDefLine( $1.sval ) == -1 ) {
				yyerror("Function: " + $1.sval + " is undefined");
				$$ = $1;
			}
			else
				$$ = CreateFunctionCall( $1, $3 );
			
			$$.alreadyPostFixed = true;
	    	$$.definitionCheck = true;
	    	$$.assignable = false;
	    }
    | postfix_expression '.' DATA
	    {
    		if( $1.type != upbeatVal.Type.ubCUE ) {
    			yyerror("Expression must be of type " + upbeatTypeName(upbeatVal.Type.ubCUE) + " to have a data member");
    		}
	    	$$ = new upbeatVal( $1.code + ".data" );
	    	$$.type = upbeatVal.Type.ubINT;
	    	$$.alreadyPostFixed = true;
	    	// Now let's do the type checking!
	    	if( !$1.definitionCheck ) {
				if( current_symbol_table.GetDefLine( $1.sval ) == -1 ) {
					yyerror("Using " + $1.sval + " without having declared it");
				}
	    	}
			$$.sval = $1.sval;
	    	$$.definitionCheck = true;
	    	$$.assignable = true;
	    }
    ;
    
input_expression
	: INPUT '.' VALUE_AT '(' expression ')'
		{
    		if( !current_symbol_table.inConductorFunc ) {
    			yyerror( "Attempting to use reserved word input while not in a Conductor function" );
    		}
			String newCode = new String("");
			newCode = newCode.concat($1.code);
			newCode = newCode.concat(".");
			newCode = newCode.concat("ValueAt(");
			newCode = newCode.concat( perform_cast($5.type, upbeatVal.Type.ubINT) );
			newCode = newCode.concat( $5.code );
			newCode = newCode.concat(")");
			
			if( $5.type != upbeatVal.Type.ubINT && $5.type != upbeatVal.Type.ubTIME ) {
				yyerror(" Expression:\n" + $5.code + "\nmust be either of type " + upbeatTypeName(upbeatVal.Type.ubINT) + " or "
						+ upbeatTypeName(upbeatVal.Type.ubTIME));
			}
			
			$$ = new upbeatVal( newCode );
			$$.type = upbeatVal.Type.ubFLOAT;
		}
    | INPUT '.' CURRENT_VALUE
		{
    		if( !current_symbol_table.inConductorFunc ) {
				yyerror( "Attempting to use reserved word input while not in a Conductor function" );
			}
	    	String newCode = new String("");
			newCode = newCode.concat($1.code);
			newCode = newCode.concat(".");
			newCode = newCode.concat("CurrentValue()");
			$$ = new upbeatVal( newCode );
			$$.type = upbeatVal.Type.ubFLOAT;
		}
    | INPUT '.' MORE
		{
    		if( !current_symbol_table.inConductorFunc ) {
				yyerror( "Attempting to use reserved word input while not in a Conductor function" );
			}
	    	String newCode = new String("");
			newCode = newCode.concat($1.code);
			newCode = newCode.concat(".");
			newCode = newCode.concat("GetMore()");
			$$ = new upbeatVal( newCode );
			$$.type = upbeatVal.Type.ubBOOL;
		}
    ;

primary_expression
    : IDENTIFIER
    	{
    		$$ = new upbeatVal($1.code);
    		$$.sval = $1.sval;
    		$$.type = current_symbol_table.GetType($1.sval);
    		$$.definitionCheck = false;
    		$$.assignable = true;
    	}
    | CURR_CUE
	    {
    		if( !current_symbol_table.inCueResponse ) {
    			yyerror("Attempting to use " + $1.code + " while not in a cue response");
    		}
    		$$ = new upbeatVal($1.code);
    		$$.sval = $1.sval;
    		$$.type = upbeatVal.Type.ubCUE;
    		$$.definitionCheck = true;
    		$$.assignable = false;
	    }
    | CONSTANT
    	{
    		$$ = new upbeatVal($1.code);
    		$$.type = $1.type;
    		$$.sval = $1.code;
    		$$.definitionCheck = true;
    		$$.assignable = false;
    	}
    | STRING_LITERAL
    	{
    		$$ = new upbeatVal($1.sval);
    		$$.sval = $1.code;
    		// Agreed, that's a little confusing.
    		$$.type = $1.type;
    		$$.definitionCheck = true;
    		$$.assignable = false;
    	}
    | '(' expression ')'
    	{
    		$$ = new upbeatVal("(" + $2.code + ")");
    		$$.type = $2.type;
    		$$.sval = $1.code;
    		$$.definitionCheck = true;
    		$$.assignable = false;
    	}
    | error ')'
	    {
	    	yyerror("Error in expression");
	    }
    ;
 
unary_operator
    : '+'
    	{
    		$$ = new upbeatVal("+");
    		$$.unaryOp = upbeatVal.UnaryOp.uopPLUS;
    	}
    | '-'
	    {
			$$ = new upbeatVal("-");
			$$.unaryOp = upbeatVal.UnaryOp.uopMINUS;
		}
    | '!'
	    {
			$$ = new upbeatVal("!");
			$$.unaryOp = upbeatVal.UnaryOp.uopNOT;
		}
    ;

argument_expression_list
	: expression
		{
    		$$ = $1;
    		$$.listTypes = new LinkedList<upbeatVal.Type>();
    		$$.listTypes.add($1.type);
    		$$.listExprs = new LinkedList<String>();
    		$$.listExprs.add($1.code);
    	}
	| argument_expression_list ',' expression
		{
			// We're not actually going to use this code...
			$$ = new upbeatVal( $1.code + ", " + $3.code );
			
			// We need to build up a list of expression codes and types
			// so we can do type checking and casting at the postfix_expression
			// level of the grammar
			$$.listTypes = $1.listTypes;
			$$.listTypes.add($3.type);
			$$.listExprs = $1.listExprs;
			$$.listExprs.add($3.code);
		}
	;
  
parameter_list
	: declaration
		{
			$$ = new upbeatVal( $1.code );
			$$.listTypes = new LinkedList< upbeatVal.Type >();
			$$.listTypes.add( $1.type );
		}
	| parameter_list ',' declaration
		{
			$$ = new upbeatVal( $1.code + ", " + $3.code );
			$$.listTypes = $1.listTypes;
			$$.listTypes.add( $3.type );
		}
	;
    
    
declaration_list
    : declaration ';'
    	{
    		$$ = new upbeatVal($1.code + ";");
    	}
    | declaration_list declaration ';'
    	{
    		$$ = new upbeatVal($1.code + "\n" + $2.code + ";");
    	}
    ;
 
declaration
    : type_specifier declarator
    	{
    		// If we've got a phrase, we need to make sure we're not declaring it in the Conductor
    		if( $1.type == upbeatVal.Type.ubPHRASE && current_symbol_table.inConductor )
    			yyerror("Cannot declare phrases in Conductor block");
    		if( $1.type == upbeatVal.Type.ubCUE && current_symbol_table.inMusician )
    			yyerror("Cannot declare cues in Musician block");
    		
    		/* First, check if variable is declared already; if not, add to symbol table */
    		if(current_symbol_table.GetType($2.sval) == null) {
    			current_symbol_table.AddSymbol($2.sval, $1.type, lexer.current_line());
    		} else {
    			yyerror("Variable " + $2.sval + " already defined on line " +
    					current_symbol_table.GetDefLine($2.sval) + " as an " +
    					current_symbol_table.GetType($2.sval));
    		}
    		
    		/* Assignment type checking and code generation */
    		if($2.obj instanceof String) {
    			// Need to do assignment
    			$$ = apply_op("=", $2.code, (String)$2.obj, $1.type, $2.type);
    		} else {
    			$$ = new upbeatVal($2.code);
    		}
    		$$.code = $1.code + " " + $$.code;
    		$$.type = $1.type;
    	}
    ;
 
declarator
    : IDENTIFIER
    	{
    		$$ = new upbeatVal($1.code);
    		$$.sval = $1.sval;
    	}
    | IDENTIFIER EQ_ASS expression
    	{
    		/* NOTE THAT ASSIGNMENT TYPE CHECKING OCCURS UP AT DECLARATION. */
    		/* TODO: (optional) mark in symbol table that it's been assigned */
    		$$ = new upbeatVal($3.type);
    		$$.sval = $1.sval;
    		$$.code = $1.code;
    		$$.obj = new String($3.code);
    	}
    ;

type_specifier
    : INT
    	{
    		$$ = new upbeatVal(upbeatVal.Type.ubINT);
    		$$.code = javaTypeName(upbeatVal.Type.ubINT);
    	}
    | FLOAT
    	{
    		$$ = new upbeatVal(upbeatVal.Type.ubFLOAT);
    		$$.code = javaTypeName(upbeatVal.Type.ubFLOAT);
    	}
    | BOOL
    	{
    		$$ = new upbeatVal(upbeatVal.Type.ubBOOL);
    		$$.code = javaTypeName(upbeatVal.Type.ubBOOL);
    	}
    | CUE
    	{
    		$$ = new upbeatVal(upbeatVal.Type.ubCUE);
    		$$.code = javaTypeName(upbeatVal.Type.ubCUE);
    	}
    | PHRASE
    	{
    		$$ = new upbeatVal(upbeatVal.Type.ubPHRASE);
    		$$.code = javaTypeName(upbeatVal.Type.ubPHRASE);
    	}
    | TIME
    	{
    		$$ = new upbeatVal(upbeatVal.Type.ubTIME);
    		$$.code = javaTypeName(upbeatVal.Type.ubTIME);
    	}
    ;
    
function_definition_list
	: function_definition
	{
    	$$ = new upbeatVal($1.code);
	}
	| function_definition_list function_definition
	{
		$$ = new upbeatVal($1.code + $2.code);
	}
	;
	
function_definition
	: IDENTIFIER '(' ')'
		{	
			// First, check if variable is declared already; if not, add to symbol table
    		if(current_symbol_table.GetFuncReturnType($1.code) == null) {
    			FunctionType fType = new FunctionType();
    			fType.SetReturnType( upbeatVal.Type.ubVOID );
    			current_symbol_table.AddFuncSymbol( $1.sval, fType, lexer.current_line());
    		} else {
    			yyerror("Function " + $1.sval + " already defined on line " +
    					current_symbol_table.GetFuncDefLine($1.code));
    		}
    		
    		SymbolTable t = new SymbolTable(current_symbol_table);
			current_symbol_table = t;
			current_symbol_table.inConductorFunc = true;
			current_symbol_table.myName = $1.sval;
		}
	compound_statement
		{
			String funcode = new String("\n");
    		$$ = new upbeatVal(upbeatVal.Type.ubVOID);
    		if($1.sval.equals("Conduct")) {
    			funcode = funcode.concat("protected ");
    		}
    		funcode = funcode.concat("void " + $1.code + "() throws Exception " + $5.code);
    		$$.code = funcode;
    		current_symbol_table = current_symbol_table.parent;
		}
    | IDENTIFIER '('
    	{	
    		// We need a new symbol table to scope arguments to the function.
			SymbolTable t = new SymbolTable(current_symbol_table);
			current_symbol_table = t;
			current_symbol_table.inConductorFunc = true;
			current_symbol_table.myName = $1.sval;
			
			// What follows is kind of weird.
			
			// Basically, we need to create a new symbol table that corresponds with the code
			// immediately inside this function call.  We want the parameters in the parameter_list
			// below to be scoped only to the function.  However, we need to know what types the parameters
			// are in order to create a symbol table entry for the function itself.  Furthermore, we need
			// to have the function scoped one level above, which is why we call the current_symbol_table.parent.
			// Finally, though, we want the function to be callable from in itself, so we need to add it
			// to the symbol table before we process the compound_statement.  Otherwise, we wouldn't
			// be able to do recursion.
    	}
    parameter_list ')'
	    {
	    	// First, check if variable is declared already; if not, add to symbol table
			if(current_symbol_table.parent.GetFuncReturnType($1.code) == null) {
				FunctionType fType = new FunctionType();
				fType.SetReturnType( upbeatVal.Type.ubVOID );
				fType.SetArgs( $4.listTypes );
				current_symbol_table.parent.AddFuncSymbol( $1.sval, fType, lexer.current_line());
			} else {
				yyerror("Function " + $1.sval + " already defined on line " +
						current_symbol_table.parent.GetFuncDefLine($1.sval));
			}
	    }
    compound_statement
    	{
    		String funcode = new String("\n");
    		$$ = new upbeatVal(upbeatVal.Type.ubVOID);
    		if($1.sval.equals("Conduct")) {
    			funcode = funcode.concat("protected ");
    		}
    		funcode = funcode.concat("void " + $1.code + "(" + $4.code + ") throws Exception " + $7.code);
    		$$.code = funcode;
        	current_symbol_table = current_symbol_table.parent;
    	}
    | type_specifier IDENTIFIER '(' ')'
	    {
	    	// First, check if variable is declared already; if not, add to symbol table
			if(current_symbol_table.GetFuncReturnType($2.code) == null) {
				FunctionType fType = new FunctionType();
				fType.SetReturnType( $1.type );
				current_symbol_table.AddFuncSymbol( $2.sval, fType, lexer.current_line());
			} else {
				yyerror("Function " + $2.sval + " already defined on line " +
						current_symbol_table.parent.GetFuncDefLine($2.sval));
			}
			
			// We need a new symbol table to scope arguments to the function.
			SymbolTable t = new SymbolTable(current_symbol_table);
			current_symbol_table = t;
			current_symbol_table.inConductorFunc = true;
			current_symbol_table.myName = $2.sval;
	    }
    compound_statement
    	{
    		$$ = new upbeatVal($1.type);
    		if($2.sval.equals("Conduct")) {
    			yyerror("Conduct function should not have a return type");
    		}
    		$$.code = "\n" + $1.code + " " + $2.code + "() throws Exception " + $6.code;
    		if( !current_symbol_table.returns ) {
    			yyerror("Function " + $2.sval + " does not return a value");
    		}
    		current_symbol_table = current_symbol_table.parent;
    	}
    | type_specifier IDENTIFIER '('
    	{
			// We need a new symbol table to scope arguments to the function.
			SymbolTable t = new SymbolTable(current_symbol_table);
			current_symbol_table = t;
			current_symbol_table.inConductorFunc = true;
			current_symbol_table.myName = $2.sval;
    	}
    parameter_list ')'
	    {
	    	/* First, check if variable is declared already; if not, add to symbol table */
			if(current_symbol_table.parent.GetFuncReturnType($2.code) == null) {
				FunctionType fType = new FunctionType();
				fType.SetReturnType( $1.type );
				fType.SetArgs( $5.listTypes );
				current_symbol_table.parent.AddFuncSymbol( $2.sval, fType, lexer.current_line());
			} else {
				yyerror("Function " + $2.sval + " already defined on line " +
						current_symbol_table.parent.GetFuncDefLine($2.sval));
			}
	    }
    compound_statement
    	{
    		$$ = new upbeatVal($1.type);
    		if($2.sval.equals("Conduct")) {
    			yyerror("Conduct function should not have a return type");
    		}
    		$$.code = "\n" + $1.code + " " + $2.code + "(" + $5.code + ") throws Exception " + $8.code;
    		if( !current_symbol_table.returns ) {
    			yyerror("Function " + $2.sval + " does not return a value");
    		}
    		current_symbol_table = current_symbol_table.parent;
    	}
    ;
 
// Musical statements!
adding_cue_statement
    : ADD expression expression ';'
    {
    	// If we're trying to add a cue to the score from a Musician, it's an error!
    	if( !current_symbol_table.inConductorFunc )
    		yyerror("Attempting to Add a cue outside the definition of a function in the Conductor");
    	
    	String newCode = new String("");
    	newCode = newCode.concat( "AddCueToScore( " );
    	newCode = newCode.concat( perform_cast( $2.type, upbeatVal.Type.ubCUE ) );
    	newCode = newCode.concat( $2.code + ", " );
    	newCode = newCode.concat( perform_cast( $3.type, upbeatVal.Type.ubTIME) );
    	newCode = newCode.concat($3.code + ");\n");
    	
    	$$ = new upbeatVal(newCode);
    }
    ;

musical_statement
    : phrase_expression ';' { $$ = new upbeatVal( $1.code + ";\n" ); }
    | phrase_control_expression ';' { $$ = new upbeatVal( $1.code + ";\n" ); }
    ;

phrase_control_expression
    : CHANGE change_parameter to_by_option expression
    	{
    		if( !current_symbol_table.inMusician )
    			yyerror("Must be in a Musician to Change Volume, Pitch, or Instrument");
    		
    		String newCode = new String("getMusicMaker().change");
    		switch ($2.changeParam ){
    		case Pitch:
    			newCode = newCode.concat("Pitch");
    			break;
    		case Volume:
    			newCode = newCode.concat("Volume");
    			break;
    		case Instrument:
    			newCode = newCode.concat("Instrument");
    			break;
    		}
    		
    		switch ($3.toOrBy ){
    		case To:
    			newCode = newCode.concat("To");
    			break;
    		case By:
    			newCode = newCode.concat("By");
    			break;
    		}
    		
    		newCode = newCode.concat("(");
    		newCode = newCode.concat( perform_cast( $4.type, upbeatVal.Type.ubINT) );
    		newCode = newCode.concat($4.code);
    		newCode = newCode.concat(", ");
    		if(current_symbol_table.inCueResponse) {
    			newCode = newCode.concat("cue.getTime()");
    		} else {
    			newCode = newCode.concat(String.format("%d", startMeasure));
    		}
    		newCode = newCode.concat(")");
    		$$ = new upbeatVal(newCode);
    	}
    ;

change_parameter
    : VOLUME { $$ = new upbeatVal( upbeatVal.ChangeParam.Volume ); }
    | INSTRUMENT { $$ = new upbeatVal( upbeatVal.ChangeParam.Instrument ); }
    | PITCH { $$ = new upbeatVal( upbeatVal.ChangeParam.Pitch ); }
    ;
 
to_by_option
    : TO { $$ = new upbeatVal( upbeatVal.ToBy.To ); }
    | BY { $$ = new upbeatVal( upbeatVal.ToBy.By ); }
    ;

phrase_expression
    : first_phrase continuing_phrases
    {
    	if( !current_symbol_table.inMusician )
    		yyerror("Attempting to play things outside a Musician");
    	
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	code = code.concat($1.code);
    	code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, $1.sval));
    	
    	if ($2 != null) {
    		code = code.concat($2.code);
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, $2.sval));    		
    	}
    	
    	if( current_symbol_table.inCueResponse )
    		code = code.concat(String.format("getMusicMaker().appendPhraseAt(%s, cue.getTime())", tempVarName));
    	else
    		code = code.concat(String.format("getMusicMaker().appendPhraseAt(%s, %d)", tempVarName, startMeasure));
    	$$ = new upbeatVal(code);
    }
    ;

first_phrase
    : repeated_phrase
    | one_time_phrase
    ;

repeated_phrase
    : DB ':' musical_phrase RRPT
    	{
    		String code = new String();
    		String tempVarName = getNextTempName();
        	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
        	for (String p : $3.listval) {
        		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, p));
        	}
    		code = code.concat(String.format("%s.setRepeat(-1);\n", tempVarName));
    		upbeatVal res = new upbeatVal(code);
    		res.sval = tempVarName;
    		$$ = res;
    	}
    | DB ':' musical_phrase RRPT constant_expression
    	{
	    	String code = new String();
			String tempVarName = getNextTempName();
	    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
	    	for (String p : $3.listval) {
	    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, p));
	    	}
			code = code.concat(String.format("%s.setRepeat(%s);\n", tempVarName, $5.code));
			upbeatVal res = new upbeatVal(code);
			res.sval = tempVarName;
			$$ = res;
    	}
    ;

one_time_phrase
    : DB musical_phrase DB
    {
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	for (String p : $2.listval) {
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, p));
    	}
		upbeatVal res = new upbeatVal(code);
		res.sval = tempVarName;
		$$ = res;
    }
    ;

continuing_phrases
    : continued_repeated_phrase continuing_phrases
    {
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	code = code.concat($1.code);
    	code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, $1.sval));
    	
    	if ($2 != null) {
    		code = code.concat($2.code);
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, $2.sval));    		
    	}
    	
    	$$ = new upbeatVal(code);
    	$$.sval = tempVarName;
    }
    | continued_one_time_phrase continuing_phrases
    {
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	code = code.concat($1.code);
    	code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, $1.sval));
    	
    	if ($2 != null) {
    		code = code.concat($2.code);
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, $2.sval));    		
    	}
    	
    	$$ = new upbeatVal(code);
    	$$.sval = tempVarName;
    }
    |
    	{
    		$$ = null;
    	}
    ;

musical_phrase
    : IDENTIFIER
		{
    		if(current_symbol_table.GetType($1.sval) == null) {
    			yyerror("Using " + $1.sval + " without having declared it");
    		}
    		$$ = new upbeatVal("");
    		$$.sval = $1.sval;
    		$$.type = current_symbol_table.GetType($1.sval);
    		if ($$.type != upbeatVal.Type.ubPHRASE) {
    			yyerror ("Using a non-phrase variable in the musical phrase play statement is not allowed");
    		}
    		$$.listval = new LinkedList<String>();
    		$$.listval.add($1.code);
		}
    | IDENTIFIER '|' musical_phrase
    	{
			if(current_symbol_table.GetType($1.sval) == null) {
				yyerror("Using " + $1.sval + " without having declared it");
			}
			if( current_symbol_table.GetType($1.sval) != upbeatVal.Type.ubPHRASE ) {
				yyerror("Using a non-phrase variable in a musical phrase play statement is not allowed");
			}
			$$ = new upbeatVal("");
			$$.sval = $1.sval;
    		$$.type = upbeatVal.Type.ubPHRASE;
			$$.listval = new LinkedList<String>();
			$$.listval.add($1.code);
			if($3.listval != null) {
				for(String p : $3.listval) {
					$$.listval.add(p);
				}
			}
    	}
    | CONSTANT
    {
    	//NOTE: ALL constants of type "ubPHRASE" are RESTS.
    	if($1.type != upbeatVal.Type.ubPHRASE) {
    		yyerror ("Musical phrases can only contain variables of type " + upbeatTypeName(upbeatVal.Type.ubPHRASE) + " or RESTs");
    	}
    	$$ = new upbeatVal("");
    	$$.listval = new LinkedList<String>();
    	$$.listval.add("MusicPhrase.getREST()");
    }
    | CONSTANT '|' musical_phrase
    {
    	//NOTE: ALL constants of type "ubPHRASE" are RESTS.
    	if($1.type != upbeatVal.Type.ubPHRASE) {
    		yyerror ("Musical phrases can only contain variables of type " + upbeatTypeName(upbeatVal.Type.ubPHRASE) + " or RESTs");
    	}
    	$$ = new upbeatVal("");
    	$$.listval = new LinkedList<String>();
    	$$.listval.add("MusicPhrase.getREST()");
    	
    	if($3.listval != null) {
			for(String p : $3.listval) {
				$$.listval.add(p);
			}
		}
    }
    ;

continued_repeated_phrase
    : ':' musical_phrase RRPT
    {
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	for (String p : $2.listval) {
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, p));
    	}
		code = code.concat(String.format("%s.setRepeat(-1);\n", tempVarName));
		upbeatVal res = new upbeatVal(code);
		res.sval = tempVarName;
		$$ = res;
    }
    | ':' musical_phrase RRPT constant_expression
    {
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	
    	for (String p : $2.listval) {
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, p));
    	}
		code = code.concat(String.format("%s.setRepeat(%s);\n", tempVarName, $4.code));
		upbeatVal res = new upbeatVal(code);
		res.sval = tempVarName;
		$$ = res;
    }
    ;

continued_one_time_phrase
    : musical_phrase DB 
    {
    	String code = new String();
		String tempVarName = getNextTempName();
    	code = code.concat(String.format("MusicPhrase %s = new MusicPhrase();\n", tempVarName));
    	for (String p : $1.listval) {
    		code = code.concat(String.format("%s.appendPhrase(%s);\n", tempVarName, p));
    	}
		upbeatVal res = new upbeatVal(code);
		res.sval = tempVarName;
		$$ = res;
    }
    ;

// Musician blocks
musician_blocks
    : musician_block
    	{
			Hashtable<String, String> musician_code = new Hashtable<String, String>();
			musician_code.put($1.sval, $1.code);
    		$$ = new upbeatVal(1);
    		// TODO: clean up the way we assign musician code
    		$$.obj = musician_code;
    		// ival indicates number of musicians
    	}
    | musician_blocks musician_block
    	{
			Hashtable<String, String> musician_code = new Hashtable<String, String>();
			Hashtable<String, String> orig;
			Enumeration<String> k;
    		// TODO: clean up the way we assign musician code
			orig = (Hashtable)$1.obj;
			k = orig.keys();
			while(k != null && k.hasMoreElements()) {
				String key = k.nextElement();
				if (key.equals($2.sval)) {
					yyerror ("can't have more than one musician with the same name");
				}
				musician_code.put(key, orig.get(key));
			}
			musician_code.put($2.sval, $2.code);
			$$ = new upbeatVal(1 + $1.ival);
			$$.obj = musician_code;
			// ival indicates number of musicians
    	}
    ;

musician_block
    : MUSICIAN STRING_LITERAL '{' 
    	{
    		SymbolTable t = new SymbolTable(current_symbol_table);
    		current_symbol_table = t;
    		current_symbol_table.inMusician = true;
    	}
    musician_body '}'
    	{
    		String gen_code = new String();
    		
    		gen_code = gen_code.concat("package upbeat.sample;\n\n" +
    				"import upbeat.Musician;\n" +
    				"import upbeat.types.Cue;\n" +
    				"import upbeat.types.MusicPhrase;\n" +
    				"\n" +
    				"public class Musician_" + $2.code + " extends Musician {\n");
    		
    		if($5.listval != null) {
    			for(String decl : $5.listval) {
    				gen_code = gen_code.concat(decl + "\n");
    			}
    		}
    		
    		try {
    		gen_code = gen_code.concat("	public Musician_" + $2.code + "() {\n" +
    				" 		// Call the Parent constructor.\n" +
    				"		super(" + getNextMusicianChannel() + ");\n" +
    				"		\n" + 
    				"		// Initializations.\n" +
    				"		try {\n" +
    				$5.sval +
    				"       } catch (Exception e) {\n" +
    				"			System.err.println(\"Did not correctly initialize Musician. \" + e +\n" +
    				"                  \"Music may sound incorrect.\");\n" +
    				"		}\n" +
    				"	}\n" +
    				$5.code +
    				"\n}\n");
    		} catch (Error e) {
    			yyerror("Error creating next musician: " + e.getMessage());
    		}
    		$$ = new upbeatVal(gen_code);
    		$$.sval = $2.code; // code has name of musician
        	current_symbol_table = current_symbol_table.parent;
    	}
    ;

musician_body
    : statement_list cue_response_list
    	{
    		String newCode = new String();
    		
	    	newCode = newCode.concat( "\n\n@Override\npublic void processCue(Cue cue) throws Exception {\n" );
	    	newCode = newCode.concat( $2.code );
	    	newCode = newCode.concat( "\n}" );
	    	
	    	$$ = new upbeatVal( newCode );
	    	$$.sval = $1.code; // statements
	    	$$.listval = null; // no declarations
    	}
    | declaration_list cue_response_list
	    {
	    	String newCode = new String();
	    	
	    	newCode = newCode.concat( "\n\n@Override\npublic void processCue(Cue cue) throws Exception {\n" );
	    	newCode = newCode.concat( $2.code );
	    	newCode = newCode.concat( "\n}" );
	    	
	    	$$ = new upbeatVal( newCode );
	    	$$.sval = ""; // no statements
	    	$$.listval = new LinkedList<String>();
    		$$.listval.add($1.code);
	    }
    | declaration_list statement_list cue_response_list
	    {
	    	String newCode = new String();
	    	
/* A little hacky, but for musicians:
 * 1. The code field contains the entire processCue method.
 * 2. The sval field contains statements which go in the constructor
 * 3. The listval probably only contains one element which is the declarations; these go at the top of the class
 */

	    	newCode = newCode.concat( "\n\n@Override\npublic void processCue(Cue cue) throws Exception {\n" );
	    	newCode = newCode.concat( $3.code );
    		newCode = newCode.concat( "\n}" );
	    	
	    	$$ = new upbeatVal( newCode );
	    	$$.sval = $2.code;
	    	$$.listval = new LinkedList<String>();
    		$$.listval.add($1.code);
	    }
    | declaration_list statement_list
    	{
    		String newCode = new String();

    		//In some cases, musicians will just want to play one thing the whole song, or just at the beginning of the song.  Might as well let them!
    		newCode = newCode.concat( "\n\n@Override\npublic void processCue(Cue cue) throws Exception {\n" );
    		// No cue responses
			newCode = newCode.concat( "\n}" );
	    	
			$$ = new upbeatVal( newCode );
			$$.sval = $2.code;
	    	$$.listval = new LinkedList<String>();
			$$.listval.add($1.code);
	    }
    ;

cue_response_list
    : cue_response
    	{
    		$$ = new upbeatVal($1.code);
    	}
    | cue_response cue_response_list
    	{
    		String newCode = new String($1.code + "\n" + $2.code + "\n");
    		$$ = new upbeatVal( newCode );
    	}
    ;

cue_response
    : STRING_LITERAL
	    {
	    	SymbolTable t = new SymbolTable(current_symbol_table);
	    	current_symbol_table = t;
	    	current_symbol_table.inCueResponse = true;
	    }
    compound_statement
    	{
    		String newCode = new String("if (cue.getName().equals(" + $1.sval + ") )\n" + $3.code + "\n");
    		$$ = new upbeatVal( newCode );
    		
    		current_symbol_table = current_symbol_table.parent;
    	}
    ;

%%
  // *******************************************
  // JAVA CODE THAT IS ADDED TO THE PARSER CLASS
  // *******************************************
  /* a reference to the lexer object */
  private Yylex lexer;
  private SymbolTable current_symbol_table;
  public static int yyerrors;
  public static int yywarnings;
  
  // This is used to determine which measure we start playing music at if a play statement
  // appears outside of processCue
  private int startMeasure = 1;
  // Total number of errors encountered
  
  private final Hashtable<String, short[][]> optable = new Hashtable<String, short[][]>();
  private final short castTable[][];
  
  // This is appended to the end of any temporary variable names we create
  // This ensures that all temporary variables have unique names.  Also, it
  // keeps track of how many there are (if we care)
  // DO NOT USE THIS DIRECTLY!  INSTEAD CALL TempIdentifier()!
  private int tempCounter;
  
  private int TempIdentifier() {
	  tempCounter++;
	  return tempCounter;
  }
  
  /* interface to the lexer */
  private int yylex () {
    int yyl_return = -1;
    try {
      yyl_return = lexer.yylex();
    }
    catch (IOException e) {
      System.err.println("IO error :"+e);
    }
    return yyl_return;
  }

  /* error reporting */
  public void yyerror (String error) {
    System.err.println ("Error: " + error + " at " + lexer.lexer_loc());
    yyerrors++;
  }
  
  /* warning reporting */
  public void yywarning (String warning) {
	  System.err.println("Warning: " + warning + " at " + lexer.lexer_loc());
	  yywarnings++;
  }

  /* lexer is created in the constructor */
  public parser(Reader r) {
    lexer = new Yylex(r, this);
    yyerrors = 0;
    yywarnings = 0;
    current_symbol_table = new SymbolTable();
    	// since we have no args, the parent is void, and it's a special top-level (global) symboltable.
    
    // This is used to check to see if a cast is legal
    // Note that casting a string to a string is illegal
    // This is because we don't have an actual keyword for string types
    /*
     * 0. Not allowed.  Give an error
     * 1. Allowed, but give a warning
     * 2. Allowed, perform cast and don't give any message
     * 3. Allowed, and no cast needed
     */
    castTable = new short[][]{
    	/* from/to	  ubVOID, ubINT, ubFLOAT, ubBOOL, ubCUE, ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{   3, 		0,		0,		0,		0,		0,		0,		0	},
        /*ubINT*/	{   0,		3,		2,		0,		0,		0,		2,		0	},
        /*ubFLOAT*/	{   0,		1,		3,		0,		0,		0,		1,		0	},
        /*ubBOOL*/	{   0,		0,		0,		3,		0,		0,		0,		0	},
        /*ubCUE*/	{   0,		0,		0,		0,		3,		0,		0,		0	},
        /*ubPHRASE*/{   0,		0,		0,		0,		0,		3,		0,		0	},
        /*ubTIME*/	{   0,		2,		1,		0,		0,		0,		3,		0	},
        /*ubSTRING*/{   0,		0,		0,		0,		0,		0,		0,		0	}
    };
  
    /*
     * 0. Not allowed
     * 1. Allowed; use both arguments as-is; result type is the same.
     * 2. Allowed; use both arguments as-is; result is java bool
     * 3. Special: assigning strings (rval) to CUE types (lval)
     * 4. Special: assigning strings (rval) to PHRASE types (lval)
     * 5. Special: cast right argument from java int to java String; result is left type
     * 6. Special: cast left argument from java int to java String; result is right type
     * 7. Allowed: cast right argument to left type; result is left type
     * 8. Allowed: cast left argument to right type; result is right type
     * 9. Allowed: cast right argument to left type; result is bool
     * 10. Allowed: cast left argument to right type; result is bool
     */
    optable.put(new String("="), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		1,		7,		0,		0,		0,		7,		0	},
    	/*ubFLOAT*/	{	0,		7,		1,		0,		0,		0,		7,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		1,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		1,		0,		0,		3	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		1,		0,		4	},
    	/*ubTIME*/	{	0,		7,		7,		0,		0,		0,		1,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("+"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		1,		8,		0,		0,		0,		8,		6	},
    	/*ubFLOAT*/	{	0,		7,		1,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		7,		0,		0,		0,		0,		1,		0	},
    	/*ubSTRING*/{	0,		5,		0,		0,		0,		0,		0,		1	}
      });
    optable.put(new String("-"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		1,		8,		0,		0,		0,		8,		0	},
    	/*ubFLOAT*/	{	0,		7,		1,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		7,		0,		0,		0,		0,		1,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("*"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		1,		8,		0,		0,		0,		8,		0	},
    	/*ubFLOAT*/	{	0,		7,		1,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		7,		0,		0,		0,		0,		1,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("/"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		1,		8,		0,		0,		0,		8,		0	},
    	/*ubFLOAT*/	{	0,		7,		1,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		7,		0,		0,		0,		0,		1,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("%"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		1,		0,		0,		0,		0,		8,		0	},
    	/*ubFLOAT*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		7,		0,		0,		0,		0,		1,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("<"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		2,		10,		0,		0,		0,		9,		0	},
    	/*ubFLOAT*/	{	0,		9,		2,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		10,		0,		0,		0,		0,		2,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String(">"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		2,		10,		0,		0,		0,		9,		0	},
    	/*ubFLOAT*/	{	0,		9,		2,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		10,		0,		0,		0,		0,		2,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("<="), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		2,		10,		0,		0,		0,		9,		0	},
    	/*ubFLOAT*/	{	0,		9,		2,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		10,		0,		0,		0,		0,		2,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String(">="), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		2,		10,		0,		0,		0,		9,		0	},
    	/*ubFLOAT*/	{	0,		9,		2,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		10,		0,		0,		0,		0,		2,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("=="), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		2,		0,		0,		0,		0,		9,		0	},
    	/*ubFLOAT*/	{	0,		0,		2,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		2,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		2,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		2,		0,		0	},
    	/*ubTIME*/	{	0,		10,		0,		0,		0,		0,		2,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("!="), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		2,		0,		0,		0,		0,		9,		0	},
    	/*ubFLOAT*/	{	0,		0,		2,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		2,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		2,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		2,		0,		0	},
    	/*ubTIME*/	{	0,		10,		0,		0,		0,		0,		2,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("&&"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubFLOAT*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		1,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });
    optable.put(new String("||"), new short[][]{
    	/* left/right	ubVOID,	ubINT,	ubFLOAT,ubBOOL,	ubCUE,	ubPHRASE,ubTIME,ubSTRING */
    	/*ubVOID*/ 	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubINT*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubFLOAT*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubBOOL*/	{	0,		0,		0,		1,		0,		0,		0,		0	},
    	/*ubCUE*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubPHRASE*/{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubTIME*/	{	0,		0,		0,		0,		0,		0,		0,		0	},
    	/*ubSTRING*/{	0,		0,		0,		0,		0,		0,		0,		0	}
      });

    
    tempCounter = 0;
  }
  
  private String upbeatTypeName( upbeatVal.Type type ) {
	  switch( type ){
	  case ubVOID:
		  return "void";
	  case ubINT:
		  return "int";
	  case ubFLOAT:
		  return "float";
	  case ubBOOL:
		  return "bool";
	  case ubCUE:
		  return "cue";
	  case ubPHRASE:
		  return "phrase";
	  case ubTIME:
		  return "time";
	  case ubSTRING:
		  return "string";
	  }
	  return "Unrecognized type";
  }
  
  private String javaTypeName( upbeatVal.Type type ) {
	  switch( type ){
	  case ubVOID:
		  return "void";
	  case ubINT:
		  return "int";
	  case ubFLOAT:
		  return "double";
	  case ubBOOL:
		  return "boolean";
	  case ubCUE:
		  return "Cue";
	  case ubPHRASE:
		  return "MusicPhrase";
	  case ubTIME:
		  return "int";
	  case ubSTRING:
		  return "String";
	  }
	  return "Unrecognized type";
  }
  
  /*private boolean is_cast_allowed( upbeatVal.Type oldType, upbeatVal.Type newType ) {
	  return castTable[oldType.ordinal()][newType.ordinal()];
  }*/
  
  private String force_cast( upbeatVal.Type oldType, upbeatVal.Type newType ) {

	  if( oldType == null ) {
		  throw new Error("Attempting to cast from an unknown type");
	  }
	  if( newType == null ) {
		  throw new Error("Attempting to cast to an unknown type");
	  }

	  String cast = new String("");
	  short i = castTable[oldType.ordinal()][newType.ordinal()];
	  if( i == 0 ) {
	  // Disallow cast
		  yyerror("Illegal cast from " + upbeatTypeName(oldType) + " to " + upbeatTypeName(newType));
		  return cast;
	  }
	  else {
		  cast = cast.concat("(" + javaTypeName(newType) + ")");
	  }
	  
	  return cast;
  }
  
  private String perform_cast( upbeatVal.Type oldType, upbeatVal.Type newType ) {
	  if( oldType == null ) {
		  throw new Error("Attempting to cast from an unknown type");
	  }
	  if( newType == null ) {
		  throw new Error("Attempting to cast to an unknown type");
	  }
	  
	  String cast = new String("");
	  short i = castTable[oldType.ordinal()][newType.ordinal()];
	  switch( i ) {
	  // Disallow cast
	  case 0:
		  yyerror("Illegal cast from " + upbeatTypeName(oldType) + " to " + upbeatTypeName(newType));
		  return cast;
	  case 1:
		  yywarning("Casting from " + upbeatTypeName(oldType) + " to " + upbeatTypeName(newType));
	  case 2:
		  cast = cast.concat("(" + javaTypeName(newType) + ")");
		  return cast;
	  case 3:
		  return cast;
	  default:
		  throw new Error("Received illegal cast table value.");
	  }
  }
  
  // This is just a dummy function used for convenience...
  private upbeatVal CreateFunctionCall( upbeatVal funcName ) {
	  upbeatVal temp = new upbeatVal();
	  temp.listTypes = new LinkedList< upbeatVal.Type >();
	  temp.listExprs = new LinkedList< String >();
	  return CreateFunctionCall( funcName, temp );
  }
  
  private upbeatVal CreateFunctionCall( upbeatVal funcName, upbeatVal args ) {
	  
	  LinkedList< upbeatVal.Type > paramTypes = current_symbol_table.GetParamTypes( funcName.sval );
	  LinkedList< upbeatVal.Type > argTypes = args.listTypes;
	  LinkedList< String > argExprs = args.listExprs;
	  
	  Iterator< upbeatVal.Type > paramIter = paramTypes.iterator();
	  Iterator< upbeatVal.Type > argIter = argTypes.iterator();
	  Iterator< String > exprIter = argExprs.iterator();
	  
	  String newCode = funcName.code;
	  newCode = newCode.concat("(");
	  
	  while( paramIter.hasNext() && argIter.hasNext() && exprIter.hasNext() ) {
		  upbeatVal.Type argType = argIter.next();
		  upbeatVal.Type paramType = paramIter.next();
		  String expr = exprIter.next();
		  // If the argument and parameter have the same types, we don't need to cast
		  if( argType == paramType ) {
			  newCode = newCode.concat( expr );
		  }
		  else {
			  newCode = newCode.concat( perform_cast( argType, paramType ) );
			  newCode = newCode.concat(expr);
		  }
			  
		  // If we still have more to go, we need to add in the commas!
		  if( paramIter.hasNext() || argIter.hasNext() || exprIter.hasNext() )
			  newCode = newCode.concat(", ");
	  }
	  
	  if( paramIter.hasNext() || argIter.hasNext() || exprIter.hasNext() ) {
		  yyerror("Call to function " + funcName.code + " has incorrect number of arguments");
	  }
	  
	  newCode = newCode.concat(")");
	  
	  upbeatVal ret = new upbeatVal(newCode);
	  ret.type = current_symbol_table.GetFuncReturnType( funcName.sval );
	  
	  return ret;
  }
  
  private upbeatVal apply_op (String op, String lcode, String rcode, 
		  upbeatVal.Type ltype, upbeatVal.Type rtype) {
	  upbeatVal p = new upbeatVal();
	  if(op == null) {
		  throw new Error("attempt to apply unknown operator");
	  }
	  if(lcode == null || rcode == null) {
		  throw new Error("attempt to apply operator " + op + " with code missing");
	  }
	  if(ltype == null || rtype == null) {
		  throw new Error("attempt to apply operator " + op + " with code missing");
	  }
	  short i = optable.get(op)[ltype.ordinal()][rtype.ordinal()];
	  switch(i) {
	  case 0:
		  yyerror("Operation " + op + " not allowed on type " + upbeatTypeName(ltype) + " (left) and " +
				  upbeatTypeName(rtype) + " (right)");
		  break;
	  case 1:
		  p.code = lcode + " " + op + " " + rcode;
		  p.type = ltype;
		  break;
	  case 2:
		  p.code = lcode + " " + op + " " + rcode;
		  p.type = upbeatVal.Type.ubBOOL;
		  break;
	  case 3:
		  p.code = lcode + " = new " + javaTypeName(upbeatVal.Type.ubCUE) + "(" + rcode + ")";
		  p.type = upbeatVal.Type.ubCUE;
		  break;
	  case 4:
		  p.code = lcode + " = new " + javaTypeName(upbeatVal.Type.ubPHRASE) + "(" + rcode + ")";
		  p.type = upbeatVal.Type.ubPHRASE;
		  break;
	  case 5:
		  p.code = lcode + " " + op + " Integer.toString(" + rcode + ")";
		  p.type = ltype;
		  break;
	  case 6:
		  p.code = "Integer.toString(" + lcode + ") " + op + " " + rcode;
		  p.type = rtype;
		  break;
	  case 7:
		  p.code = lcode + " " + op + perform_cast(rtype, ltype) + rcode;
		  p.type = ltype;
		  break;
	  case 8:
		  p.code = perform_cast(ltype, rtype) + lcode + " " + op + rcode;
		  p.type = rtype;
		  break;
	  case 9:
		  p.code = lcode + " " + op + perform_cast(rtype, ltype) + rcode;
		  p.type = upbeatVal.Type.ubBOOL;
		  break;
	  case 10:
		  p.code = perform_cast(ltype, rtype) + lcode + " " + op + rcode;
		  p.type = upbeatVal.Type.ubBOOL;
		  break;
	  default:
		  yyerror("Undefined operation with given operator and types; this really shouldn't happen");
	  }
	  
	  // We're going to fully parenthesize our expressions.  However, we don't want to parenthesize our
	  // assignment expressions or else we'll get code like: int (foo = 2);
	  if( !op.equals("=") )
		  p.code = "(" + p.code + ")";
	  return p;
  }

  private static long getNextTempName = 0;
  private static String getNextTempName()
  {
	  return "t_up_" + (getNextTempName++);
  }
  
  private static int musicianChannelCounter = 0;
  private static int getNextMusicianChannel() {
	  int chan = musicianChannelCounter++;
	  if(chan == 9) {
		  chan = musicianChannelCounter++;
	  }
	  if(chan > 15) {
		  throw new Error("Upbeat currently supports only 15 musicians to comply with MIDI restrictions");
	  }
	  return chan;
  }
  
  private String generate_conductor_code(String declarations, String statements, String functions) {
  	String compiled_code = new String("");
	compiled_code = compiled_code.concat(
			"package upbeat.sample;\n\n" +
			"import upbeat.Conductor;\n" +
			"import upbeat.Manager;\n" +
			"import upbeat.types.Cue;\n\n" +
			"public class CompiledConductor extends Conductor {\n" +
			" 	// Conductor member variable declarations will go here\n" +
			declarations +
			"\n" +
			"	CompiledConductor() {\n" +
			"		super();\n\n" +
			"		// Conductor member variable initializations will go here\n" +
			statements +
			"\n" +
			"	}\n\n" +
			"	CompiledConductor( Manager man ) {\n" +
			"		super(man);\n" +
			"\n" +		
			"		// Conductor member variable initializations will go here\n" +
			statements +
			"	}\n\n" +
			functions +
			"}");

	return compiled_code;
  }
  
  /* here's how we use the lexer/parser */
  public static void main(String args[]) throws IOException {
	  int parse_retval = -1;
	  parser yyparser = new parser(new FileReader(args[0]));
	  yyparser.yydebug = false;
	  
	  String destDir = "upbeat/sample/";
	  File fdestDir = new File(destDir);
	  try {
		  if(!fdestDir.exists()) {
			  fdestDir.mkdirs();
		  }
	  } catch (Exception e) {
		  System.err.println("Error creating destination directory: " + destDir);
	  }

	  System.out.println("Compiling Upbeat file:  " + args[0]);

	  parse_retval = yyparser.yyparse();
	  if(parse_retval == 0 && yyerrors == 0) {
		  try {
			  /* Write code files */
			  /* Application */
			  FileWriter fstream = new FileWriter(destDir + "CompiledUpbeatApplication.java");
			  BufferedWriter out = new BufferedWriter(fstream);
			  System.out.println("Writing CompiledUpbeatApplication.java ...");
			  out.write(yyparser.yyval.code);
			  out.close();
			  /* Conductor */
			  fstream = new FileWriter(destDir + "CompiledConductor.java");
			  out = new BufferedWriter(fstream);
			  System.out.println("Writing CompiledConductor.java ...");
			  out.write(yyparser.yyval.sval);
			  out.close();

			  /* Musicians */
			  Enumeration<String> k;
			  Hashtable<String, String> musicians = (Hashtable)yyparser.yyval.obj;
			  k = musicians.keys();
			  while(k != null && k.hasMoreElements()) {
				  String key = k.nextElement();
				  fstream = new FileWriter(destDir + "Musician_" + key + ".java");
				  out = new BufferedWriter(fstream);
				  System.out.println("Writing Musician_" + key + ".java ...");
				  out.write(musicians.get(key));
				  out.close();
			  }
		  } catch (Exception e) {
			  System.err.println("Error in writing out code: " + e);
		  }
		  System.out.println("Successful parse with " + yyerrors + " errors and " + yywarnings + " warnings!\n Resulting code written to: " + fdestDir.getAbsolutePath() );
	  } else {
		  System.out.print("Parse error.  Returned " + parse_retval + " with " + yyerrors + " errors and " + yywarnings + " warnings.\n");
	  }
	  
	  System.exit(yyerrors);
  }
